#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <arpa/inet.h>

#define _FL_    __FILE__,__func__,__LINE__
#define updError(fmt, args...) \
        printf("%s:%s:%u--->"fmt, _FL_, ##args);\
        printf("\n")

#include "ralist.h"
#include "updTcpService.h"

#include "security.c"

typedef struct dspPub
{
    char  dspIp[16];
    short dspPort;
    char  service[32];
    char  srvIp[16];
    short srvPort;
}dspPub;


int dspConnect(const char *ip, short port)
{
    int fd  = 0;
    int opt = 1;
    struct sockaddr_in srvaddr = {};

    if((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
        updError("socket:%s", strerror(errno));
        return -1;
    }

    if(-1 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))){
        close(fd);
        updError("setsockopt error:%s", strerror(errno));
        return -1;
    }

    srvaddr.sin_family      = AF_INET;
    srvaddr.sin_port        = htons(port);
    srvaddr.sin_addr.s_addr = inet_addr(ip);

    if(-1 == connect(fd, (struct sockaddr *)&srvaddr, sizeof(srvaddr))){
        close(fd);
        updError("connect:%s", strerror(errno));
        return -1;
    }

    return fd;
}


void dspClose(int sockfd)
{
    close(sockfd);
}


int dspSend(int sockfd, char *buf, int len, unsigned tmout)
{
    int left = len;
    int ret  = 0;
    long lefttime = 0;
    time_t  start = time(NULL);
    fd_set  wfds;
    struct  timeval tv;

    while(left){
        lefttime = start + (int)tmout - time(NULL);
        if(lefttime < 0){
            updError("recv timeout");
            return -1;
        }

        FD_ZERO(&wfds);
        FD_SET(sockfd, &wfds);
        tv.tv_usec = 0;
        tv.tv_sec  = lefttime;

        ret = select(sockfd + 1, NULL, &wfds, NULL, &tv);
        if(ret > 0){
            if(FD_ISSET(sockfd, &wfds)){
                ret = send(sockfd, buf, left, MSG_DONTWAIT|MSG_NOSIGNAL);
                if(ret < 0){
                    if(EINTR == errno || EAGAIN == errno){
                        continue;
                    }
                    if(EPIPE != errno) updError("send error:%s", strerror(errno));
                    return -1;
                }
                buf  += ret;
                left -= ret;
            }
        }
        else if(0 == ret){
            updError("send timeout");
            return -1;
        }
        else{
            updError("select error:%s", strerror(errno));
            return -1;
        }
    }

    return 0;
}


int dspRecv(int sockfd, char *buf, int len, unsigned tmout)
{
    int left = len;
    int ret  = 0;
    long lefttime = 0;
    time_t  start = time(NULL);
    fd_set  rfds;
    struct  timeval tv;

    while(left){
        lefttime = start + (int)tmout - time(NULL);
        if(lefttime < 0){
            updError("recv timeout");
            return -1;
        }

        FD_ZERO(&rfds);
        FD_SET(sockfd, &rfds);
        tv.tv_usec = 0;
        tv.tv_sec  = lefttime;

        ret = select(sockfd + 1, &rfds, NULL, NULL, &tv);
        if(ret > 0){
            if(FD_ISSET(sockfd, &rfds)){
                ret = recv(sockfd, buf, left, MSG_DONTWAIT);
                if(0 == ret){
                    updError("peer close when recv :%s", strerror(errno));
                    return -1;
                }
                if(ret < 0){
                    if(EINTR == errno || EAGAIN == errno){
                        continue;
                    }
                    updError("recv error:%s", strerror(errno));
                    return -1;
                }
                buf  += ret;
                left -= ret;
            }
        }
        else if(0 == ret){
            updError("recv timeout");
            return -1;
        }
        else{
            updError("select error:%s", strerror(errno));
            return -1;
        }
    }

    return 0;
}


void * __dspPubService(void *args)
{
    dspPub *info = args;
    int fd = -1;
    int tmval = 2;
    char buf[256];
    char resp[256];
    int  len;
    tcpHead *head = (tcpHead *)buf;
    srvAddress *addr = (srvAddress *)(buf+sizeof(tcpHead));

    addr->ip = inet_addr(info->srvIp);
    addr->port = htons(info->srvPort);
    head->cmd = SRV_REGISTER;
    head->bodylen = sizeof(srvAddress);
    head->seckey  = 0;
    head->ecode   = 0;
    strcpy(head->service, info->service);
    
    len = sizeof(tcpHead) + sizeof(srvAddress);
    xorencrypt(buf, len);

    while(1){
        do{
            if(fd < 0){
                fd = dspConnect(info->dspIp, info->dspPort);
                if(fd < 0){
                    updError("连接调度器错误[%s:%d]", info->dspIp, info->dspPort);
                }
                else{
                    if(-1 == dspSend(fd, buf, len, 10)){
                        updError("dspSend error");
                        close(fd);
                        fd = -1;
                        break;
                    }
                    if(-1 == dspRecv(fd, resp, sizeof(tcpHead), 10)){
                        updError("dspRecv error");
                        close(fd);
                        fd = -1;
                        break;
                    }
                    xordecrypt(resp, sizeof(tcpHead));
                    head = (tcpHead *)resp;
                    if(0 != head->ecode){
                        updError("发布响应:0 != head->ecode");
                        close(fd);
                        fd = -1;
                        break;
                    }
                }    
            }
            else{
                char heart = 1;
                if(-1 == dspSend(fd, &heart, 1, 10)){
                    close(fd);
                    fd = -1;
                    break;
                }
            }
        }while(0);

        sleep(tmval);
    }

    return NULL;
}



int dspPubService(const char *dspIp, short dspPort, 
                  const char *service, const char *srvIp, short srvPort)
{
    static dspPub args;
    pthread_t thid;

    /*对参数进行合法性检查TODO*/
    args.dspPort = dspPort;
    args.srvPort = srvPort;
    strcpy(args.dspIp, dspIp);
    strcpy(args.srvIp, srvIp);
    strcpy(args.service, service);

    if(0 != pthread_create(&thid, NULL, __dspPubService, &args)){
        updError("pthread_create error:%s", strerror(errno));
        return -1;
    }

    return 0;
}

extern int isBigEndianness(void);
extern void endiannessToLittle(unsigned char *buf, size_t size);
extern void endiannessToBig(unsigned char *buf, size_t size);
int dspGetService(const char *dspIp, short dspPort, 
                  const char *service, char *srvIp, short *srvPort)
{
    int fd;
    char buf[256];
    uint32_t bodylen;
    tcpHead *head = (tcpHead *)buf;
    srvAddress *addr;

    fd = dspConnect(dspIp, dspPort);
    if(fd < 0){
        updError("连接调度器错误[%s:%d]", dspIp, dspPort);
        return -1;
    }

    head->cmd = TCP_GET_ADDR;
    head->bodylen = 0;
    head->seckey  = 0;
    head->ecode   = 0;
    strcpy(head->service, service);
    if(isBigEndianness()){
        endiannessToLittle((unsigned char*)&head->cmd, sizeof(uint32_t));
        endiannessToLittle((unsigned char*)&head->bodylen, sizeof(uint32_t));
        endiannessToLittle((unsigned char*)&head->seckey, sizeof(uint32_t));
        endiannessToLittle((unsigned char*)&head->ecode, sizeof(uint32_t));
    }

    xorencrypt(buf, sizeof(tcpHead));
    if(-1 == dspSend(fd, buf, sizeof(tcpHead), 10)){
        updError("dspSend error");
        close(fd);
        return -1;
    }

    if(-1 == dspRecv(fd, buf, sizeof(tcpHead), 10)){
        updError("dspRecv error");
        close(fd);
        return -1;
    }
    xordecrypt(buf, sizeof(tcpHead));

    head = (tcpHead *)buf;
    if(isBigEndianness()){
        endiannessToBig((unsigned char*)&head->cmd, sizeof(uint32_t));
        endiannessToBig((unsigned char*)&head->bodylen, sizeof(uint32_t));
        endiannessToBig((unsigned char*)&head->seckey, sizeof(uint32_t));
        endiannessToBig((unsigned char*)&head->ecode, sizeof(uint32_t));
    }
    if(0 != head->ecode || head->bodylen != sizeof(srvAddress)){
        updError("response error");
        close(fd);
        return -1;
    }
    bodylen = head->bodylen;
    if(-1 == dspRecv(fd, buf, bodylen, 10)){
        updError("dspRecv error");
        close(fd);
        return -1;
    }

    close(fd);

    xordecrypt(buf, bodylen);

    addr = (srvAddress *)buf;
    *srvPort =  ntohs(addr->port);
    strcpy(srvIp, inet_ntoa(*(struct in_addr *)&addr->ip));

    return 0;
}


#if 0
int main(void)
{
    dspPubService("192.168.154.128", 5001, "updServer", "192.168.154.128", 3000);
    while(1)sleep(1000);
/*    
    char ip[16] = {};
    short port  = 0;

    dspGetService("192.168.154.128", 5001, "updServer", ip, &port);

    printf("%s:%d\n", ip, port);
*/    

    return 0;
}
#endif
